#include "soft_types.h"
#include <pthread.h>
#include <sys/select.h>

#include "ThreadPool/ThreadPool.h"
#include "Timer/Timer.h"

/* Timer period table */
static int TimerPeriodTable[TIMER_NUM] = {
    1000,       /**< TIMER_1HZ 1 second */
    500,        /**< TIMER_2HZ 500 ms */
    250,        /**< TIMER_4HZ 250 ms*/
    100,        /**< TIMER_10HZ 100 ms*/
    50,         /**< TIMER_20HZ 50ms */
    5000,       /**< TIMER_5S 5 seconds */
    10000,	/**< TIMER_10S 10 seconds */
    30000       /**< TIMER_30S 30 seconds*/
};

static APPLIB_TIMER_s Timers[TIMER_NUM] = {0};

static THREAD_POOL_s *pstTimerPool = NULL;
static pthread_mutex_t  stCfgMutex;

static ULONG AppLibTimer_Handler(APPLIB_TIMER_s *pstCurTimer);

static void *AppLibTimer_TimerHandler(void *param)
{
    APPLIB_TIMER_s *pstCurTimer = (APPLIB_TIMER_s *)param;
    struct timeval tv;
    int err = 0;
    
    while (pstCurTimer->HandlerNum > 0)
    {
        memset(&tv, 0, sizeof(tv));
        tv.tv_sec = pstCurTimer->iTime / 1000;
        tv.tv_usec = (pstCurTimer->iTime % 1000) * 1000;
        
        err = select(0, NULL, NULL, NULL, &tv);
        if (err < 0 && errno == EINTR)
        {
            break;
        }
        
        AppLibTimer_Handler(pstCurTimer);
    }
    
    return NULL;
}

static int AppLibTimer_ClearTimer(int tid)
{
    int ReturnValue = 0;

    /* Sanity check */
    if ((tid < 0) || (tid >= TIMER_NUM)) {
        return -1;
    }

    pthread_mutex_lock(&stCfgMutex);

    /* Stop and delete the timer */
    if (Timers[tid].Valid) {
        memset(&Timers[tid], 0, sizeof(APPLIB_TIMER_s));
    }

    pthread_mutex_unlock(&stCfgMutex);

    return ReturnValue;
}

static int AppLibTimer_SetTimer(int tid)
{
    int ReturnValue = 0;

    LOG_D("start\n");

    /* Sanity check */
    if ((tid < 0) || (tid >= TIMER_NUM)) {
        return -1;
    }

    pthread_mutex_lock(&stCfgMutex);

    /* Create a timer hander */
    ReturnValue = ThreadPool_add_job(pstTimerPool, AppLibTimer_TimerHandler, &Timers[tid]);
    if (ReturnValue != STAT_SUCCEED) {
        LOG_E("ThreadPool_add_job Failure : %d\n", ReturnValue);
        ReturnValue = -1;
    } else {
        Timers[tid].Valid = 1;;
    }
    pthread_mutex_unlock(&stCfgMutex);

    LOG_D("end\n");

    return ReturnValue;
}

ULONG AppLibTimer_Init(void)
{
    pstTimerPool = ThreadPool_init(TIMER_NUM, TIMER_NUM);
    if (pstTimerPool == NULL)
    {
        LOG_E("ThreadPool_init failed\n");
        return STAT_ERR_TIMER_POOL_CREATE;
    }
    
    /* pthread mux */
    pthread_mutex_init(&stCfgMutex, NULL);
    
    // TIMER_CHECK is used for auto-poweroff feature
    // Should not be unregistered in any cases
    memset(Timers, 0, sizeof(Timers));
    
    return STAT_SUCCEED;
}

ULONG AppLibTimer_UnregisterAll(void)
{
    int i = 0, j = 0;

    LOG_D("start\n");

    /* TIMER_CHECK = 0 is the auto-poweroff timer */
    for (i = 0; i < TIMER_NUM; i++) {
        for (j = 0; j < MAX_TIMER_HANDLER; j++) {
            Timers[i].Handler[j] = NULL;
        }
        Timers[i].HandlerNum = 0;
        AppLibTimer_ClearTimer(i);
    }

    ThreadPool_destroy(pstTimerPool);

    LOG_D("end\n");
    return 0;
}

ULONG AppLibTimer_Register(INT32 tid, AppTimer_Handler handler)
{
    int i = 0, Found = 0;
    APPLIB_TIMER_s *CurrentTimer = NULL;

    LOG_D("start: tid = %d / Handler = %p\n", tid, handler);

    if (tid >= TIMER_NUM) 
    {
        // no such timer
        LOG_E("No timer id %d\n", tid);
        return -1;
    }

    if (handler == NULL) 
    {
        // NULL Handler
        LOG_E("Timer handler is NULL\n");
        return -1;
    }

    CurrentTimer = &(Timers[tid]);

    Found = 0;
    for (i = 0; i < MAX_TIMER_HANDLER; i++) 
    {
        if (handler == CurrentTimer->Handler[i]) 
        {
            // Handler already exists
            LOG_E("This handler already registered\n");
            Found = 1;
            break;
        }
    }

    if (!Found) {
        for (i = 0; i < MAX_TIMER_HANDLER; i++) 
        {
            if (CurrentTimer->Handler[i] == NULL) 
            {
                // Empty slot Found. Insert Handler
                CurrentTimer->Handler[i] = handler;
                CurrentTimer->HandlerNum++;
                LOG_I("i = %d, Time %d\n", i, TimerPeriodTable[tid]);
                CurrentTimer->iTime = TimerPeriodTable[tid];
                if (!CurrentTimer->Valid) 
                {
                    LOG_I("Timer %d is inValid. Create timer id %d\n", tid, tid);
                    AppLibTimer_SetTimer(tid);
                }
                
                break;
            }
        }
    }

    LOG_D("end\n");

    return 0;
}

ULONG AppLibTimer_Unregister(INT32 tid, AppTimer_Handler handler)
{
    int i = 0, Found = 0;
    APPLIB_TIMER_s *CurrentTimer = NULL;

    LOG_D("start: tid = %d / Handler = %p\n", tid, handler);

    if (tid >= TIMER_NUM) {
        // No such timer
        LOG_E("No such timer id %d\n", tid);
        return -1;
    }

    if (handler == NULL) {
        // NULL Handler
        LOG_E("Timer handler is NULL\n");
        return -1;
    }

    CurrentTimer = &(Timers[tid]);

    if (CurrentTimer->HandlerNum == 0) {
        // No Handlers in timer
        LOG_E("No timer handler is registered\n");
        return -1;
    }

    Found = 0;
    for (i=0; i<MAX_TIMER_HANDLER; i++) {
        if (handler == CurrentTimer->Handler[i]) {
            Found = 1;
            CurrentTimer->Handler[i] = NULL;
            CurrentTimer->HandlerNum--;
            handler();
            break;
        }
    }

    if (Found) {
        if (CurrentTimer->HandlerNum == 0) {
            // all Handlers removed in timer. clear timer
            LOG_I("No handler under timer %d. Delete timer %d\n", tid, tid);
            AppLibTimer_ClearTimer(tid);
        }
    }

    LOG_D("end\n");
    return 0;
}

static ULONG AppLibTimer_Handler(APPLIB_TIMER_s *pstCurTimer)
{
    int i = 0;

    if (!pstCurTimer->Valid) 
    {
        // timer is not Valid
        LOG_D("Timer is invalid\n");
        return -1;
    }

    if (pstCurTimer->HandlerNum == 0) 
    {
        // no Handlers in timer
        LOG_D("No timer handler is registered\n");
        return -1;
    }

    for (i=0; i<MAX_TIMER_HANDLER; i++) 
    {
        if (pstCurTimer->Handler[i] != NULL) 
        {
            pstCurTimer->Handler[i]();
        }
    }

    return 0;
}
